package main

import (
	"container/list"
	"fmt"
	"log"
	"math/rand"
	"sync"
	"time"
)

const (
	typeSuccess int = 1
	typeFail    int = 2
)

type DefaultMetricCollector struct {
	successes int64
	failures  int64
}

type SlideWindow struct {
	bucket  int   //桶数
	curKey  int64 //当前key
	Buckets map[int64]*DefaultMetricCollector
	data    *list.List
	//Mutex   *sync.RWMutex
	sync.RWMutex
}

func NewSlideWindow(bucket int) *SlideWindow {
	sw := &SlideWindow{}
	sw.bucket = bucket
	sw.data = list.New()
	return sw
}

func (sw *SlideWindow) getCurrentBucket(now int64) *DefaultMetricCollector {
	// 先得到当前的 timestamp
	var bucket *DefaultMetricCollector
	var ok bool
	// 判断是否存在，不存在则创建
	if bucket, ok = sw.Buckets[now]; !ok {
		bucket = &DefaultMetricCollector{}
		if sw.Buckets != nil {
			sw.Buckets[now] = bucket
		}
	}

	return bucket
}

func (sw *SlideWindow) removeOldBuckets() {
	now := time.Now().Unix() - 10

	for timestamp := range sw.Buckets {
		log.Printf("now timestamp==%v\n", now, timestamp)
		if timestamp <= now {
			delete(sw.Buckets, timestamp)
		}
	}
}

func (sw *SlideWindow) Increment(i int64, t int) {
	if i == 0 {
		return
	}

	sw.Lock()
	defer sw.Unlock()
	// 先得到当前timestamp的bucket
	now := time.Now().Unix()
	b := sw.getCurrentBucket(now)

	if sw.curKey != now {
		sw.data.PushBack(sw.Buckets[now])
		delete(sw.Buckets, sw.curKey)
		sw.curKey = now
		if sw.data.Len() > sw.bucket {
			for i := 0; i <= sw.data.Len()-sw.bucket; i++ {
				sw.data.Remove(sw.data.Front())
			}
		}
	}

	switch t {
	case typeSuccess:
		b.successes += i
	case typeFail:
		b.failures += i
	default:
		log.Fatal("error type")
	}
	// 删除掉旧的
	sw.removeOldBuckets()
}

func (sw *SlideWindow) Len() int {
	return sw.data.Len()
}

func (sw *SlideWindow) Data(space int) []*DefaultMetricCollector {
	sw.RLock()
	defer sw.RUnlock()
	var data []*DefaultMetricCollector
	var num = 0
	var m = &DefaultMetricCollector{}
	log.Println(fmt.Sprintf("sw.data.Front()=%+v",
		sw.data.Front()))
	for i := sw.data.Front(); i != nil; i = i.Next() {
		one := i.Value.(*DefaultMetricCollector)
		if one != nil {
			log.Println(fmt.Sprintf("one.successes=%+v one.failures=%+v",
				one.successes, one.failures))
			m.successes += one.successes
			m.failures += one.failures
		}
		log.Println("==============r=", num%space)
		if num%space == 0 {
			data = append(data, m)
			m = &DefaultMetricCollector{}
		}
		num++
	}
	return data
}

func TestSlideWindow() {
	rand.Seed(time.Now().UnixNano())
	sw := NewSlideWindow(100)
	var r int
	for i := 0; i < 100; i++ {
		r = rand.Intn(2)
		r += 1
		log.Println(fmt.Sprintf("type=%+v", r))
		sw.Increment(1, r)
		time.Sleep(time.Duration(rand.Intn(20)) * time.Millisecond)
	}
	log.Println(fmt.Sprintf("1秒的bucket长度%+v", sw.Len()))
	for _, item := range sw.Data(3) {
		log.Println(fmt.Sprintf("successes=%+v failures=%+v", item.successes, item.failures))
	}
	for _, item := range sw.Data(5) {
		log.Println(fmt.Sprintf("successes=%+v failures=%+v", item.successes, item.failures))
	}
}

func main() {
	TestSlideWindow()
}
